package ar.edu.itba.pod.legajo45129.balance.api;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;

import org.apache.log4j.Logger;

import ar.edu.itba.balance.api.AgentsTransfer;
import ar.edu.itba.balance.api.NodeAgent;
import ar.edu.itba.event.EventInformation;
import ar.edu.itba.event.RemoteEventDispatcher;
import ar.edu.itba.node.Node;
import ar.edu.itba.node.NodeInformation;
import ar.edu.itba.pod.agent.runner.Agent;
import ar.edu.itba.pod.agent.runner.Simulation;
import ar.edu.itba.pod.legajo45129.agentSimulation.DistributedMultithreadEventDispatcher;
import ar.edu.itba.pod.legajo45129.agentSimulation.DistributedSimulation;
import ar.edu.itba.pod.legajo45129.manager.NodeManager;

import com.google.common.base.Preconditions;

public class AgentsTransferImpl implements AgentsTransfer {

	protected static final int QUEUE_SIZE = 256;
	
	private static Logger logger = Logger.getLogger(AgentsTransferImpl.class);

	public AgentsTransferImpl() throws AccessException, RemoteException {
		UnicastRemoteObject.exportObject(this, 0);
		NodeInformation localNode = NodeManager.getInstance()
				.getNodeInformation();
		Registry registry;
		registry = LocateRegistry.getRegistry(localNode.host(),
				localNode.port());
		if (registry == null) {
			registry = LocateRegistry.createRegistry(localNode.port());
		}

		registry.rebind(Node.AGENTS_TRANSFER, this);
	}

	@Override
	public void runAgentsOnNode(List<NodeAgent> agents) throws RemoteException {
		DistributedSimulation simulation = (DistributedSimulation) NodeManager
				.getInstance().getSimulation();

		NodeInformation nodeFromStopAndGet = agents.get(0).node();

		RemoteEventDispatcher remoteEventDispatcher;
		try {
			remoteEventDispatcher = NodeManager.getInstance()
					.getRemoteEventDispatcher(nodeFromStopAndGet.host(),
							nodeFromStopAndGet.port());
			RemoteEventDispatcher localDispatcher = NodeManager.getInstance().getRemoteEventDispatcher();

			for (EventInformation ei : remoteEventDispatcher
					.newEventsFor(NodeManager.getInstance().getNodeInformation())) {
				localDispatcher.publish(ei);
			}
			
			for (NodeAgent na: agents) {
				simulation.add(na.agent());
				BlockingQueue<Object> queue = remoteEventDispatcher.moveQueueFor(na.agent());
				DistributedMultithreadEventDispatcher dispatcher = (DistributedMultithreadEventDispatcher) simulation.dispatcher();
				dispatcher.setAgentQueue(na.agent(), queue);
			}
		} catch (NotBoundException e1) {
			logger.warn(e1);
		} catch (InterruptedException e) {
			logger.warn(e);
		}

	}

	@Override
	public int getNumberOfAgents() throws RemoteException {
		return NodeManager.getInstance().getSimulation().agentsRunning();
	}

	@Override
	public List<NodeAgent> stopAndGet(int numberOfAgents)
			throws RemoteException {
		Preconditions.checkArgument(numberOfAgents > 0);
		Simulation simulation = NodeManager.getInstance().getSimulation();
		List<Agent> agentsRunning = simulation.getAgentsRunning();
		List<NodeAgent> ret = new ArrayList<NodeAgent>(numberOfAgents);
		NodeInformation node = NodeManager.getInstance().getNodeInformation();
		if (agentsRunning.size() >= numberOfAgents) {
			for (int i = 0; i < numberOfAgents; i++) {
				Agent agent = agentsRunning.get(i);
				ret.add(new NodeAgent(node, agent));
				simulation.remove(agent);
			}
		}

		return ret;
	}

}
